
use mio::net::UdpSocket;
use mio::{Events, Interest, Poll, Token};
use std::net::SocketAddr;
use std::ops::DerefMut;
use std::sync::mpsc::channel;
use std::sync::{Arc, Mutex};
use std::thread::spawn;
use std::time::Duration;
#[cfg(feature = "server")]
#[derive(Clone, Debug)]
enum CMD {
    RecvClient1,
    RecvClient2,
    SendCli1ToCli2,
    SendCli2ToCli1,
}

#[cfg(feature = "server")]
const SERVER: Token = Token(0);
#[cfg(feature = "server")]
const ADDR: &str = "192.168.2.112:7777";
#[cfg(feature = "server")]
fn main() -> Result<(), std::io::Error> {
    use std::collections::HashMap;
    use std::thread::sleep;
    use std::usize;
    use bincode::config;

    let server_socket = Arc::new(Mutex::new(UdpSocket::bind(ADDR.parse().unwrap())?));
    let arc_csock = server_socket.clone();

    let poll = Arc::new(Mutex::new(Poll::new().unwrap()));
    let poll_c = poll.clone();

    let (nsx, nrx) = channel::<u8>();
    let (msx, mrx) = channel::<CMD>();
    poll_c
        .lock()
        .unwrap()
        .registry()
        .register(
            arc_csock.lock().unwrap().deref_mut(),
            SERVER,
            Interest::READABLE,
        )
        .unwrap();
    spawn(move || {
        let cmds_loop = [
            CMD::RecvClient1,
            CMD::RecvClient2,
            CMD::SendCli1ToCli2,
            CMD::SendCli2ToCli1,
        ];
        let chang_pool_state = |ins: Interest| {
            poll_c
                .lock()
                .unwrap()
                .registry()
                .reregister(arc_csock.lock().unwrap().deref_mut(), SERVER, ins)
                .unwrap();
        };
        let mut index: usize = 0;
        loop {
            match cmds_loop[index].clone() {
                CMD::RecvClient1 => {
                    chang_pool_state(Interest::READABLE);
                    index += if nrx.recv_timeout(Duration::from_millis(2)).is_ok() {
                        1
                    } else {
                        0
                    };
                }
                CMD::RecvClient2 => {
                    chang_pool_state(Interest::READABLE);
                    index += if nrx.recv_timeout(Duration::from_millis(2)).is_ok() {
                        1
                    } else {
                        0
                    };
                }
                CMD::SendCli1ToCli2 => {
                    sleep(Duration::from_millis(2));
                    let _ = msx.send(CMD::SendCli1ToCli2);
                    chang_pool_state(Interest::WRITABLE);

                    index += if nrx.recv_timeout(Duration::from_millis(2)).is_ok() {
                        1
                    } else {
                        0
                    };
                }
                CMD::SendCli2ToCli1 => {
                    sleep(Duration::from_millis(2));
                    let _ = msx.send(CMD::SendCli2ToCli1);
                    chang_pool_state(Interest::WRITABLE);
                    index -= if nrx.recv_timeout(Duration::from_millis(2)).is_ok() {
                        1
                    } else {
                        0
                    };
                }
            }
        }
    });

    let mut events = Events::with_capacity(128);

    let mut client_hash = HashMap::new();
    loop {
        poll.lock()
            .unwrap()
            .poll(&mut events, Some(Duration::from_millis(1)))?;

        for event in events.iter() {
            let mut client_buf = [0; 1500];
            if event.is_readable() {
                println!("event.is_readable try get lock");
                let guard = server_socket.lock().unwrap();
                println!("get guard lock");
                match guard.recv_from(client_buf.as_mut_slice()) {
                    Ok((_, addr)) => {
                        println!("{:?}", addr);
                        let pre = client_hash.len();
                        client_hash.insert(
                            addr,
                            bincode::encode_to_vec(
                                (addr.ip().to_string(), addr.port()),
                                config::standard(),
                            )
                            .unwrap(),
                        );
                        if client_hash.len() > pre {
                            println!("{:?}", client_hash);
                            let _ = nsx.send(1);
                        }
                    }
                    Err(e) => {
                        println!("{}", e.to_string());
                    }
                };
            } else if event.is_writable() {
                println!("event.is_writable");
                let collec = client_hash
                    .clone()
                    .into_iter()
                    .collect::<Vec<(SocketAddr, Vec<u8>)>>();

                let send_data = |mut collec_index: usize| {
                    println!("send data");
                    if let Err(e) = server_socket
                        .lock()
                        .unwrap()
                        .connect(collec[collec_index].0)
                    {
                        println!("{}", e.to_string());
                    } else {
                        collec_index = (collec_index + 1) % 2;
                        if let Err(e) = server_socket
                            .lock()
                            .unwrap()
                            .send(collec[collec_index].1.as_slice())
                        {
                            println!("{}", e.to_string());
                        } else {
                            let _ = nsx.send(1);
                        };
                    };
                };
                let r_cmd = mrx.recv_timeout(Duration::from_millis(2));
                println!("{:?}", r_cmd);
                match r_cmd {
                    Ok(cmd) => match cmd {
                        CMD::SendCli1ToCli2 => {
                            send_data(0);
                        }
                        CMD::SendCli2ToCli1 => {
                            send_data(1);
                        }
                        _ => {}
                    },
                    Err(e) => {
                        println!("{}", e.to_string())
                    }
                };
            }
        }
    }
}
#[cfg(feature = "client")]
const MY_TOKEN: Token = Token(0);
#[cfg(feature = "client")]
const ADDR: &str = "192.168.5.5:7777";
#[cfg(feature = "client")]
const SERVER_ADDR: &str = "192.168.2.112:7777";
#[cfg(feature = "client")]
#[derive(Clone, Debug)]
enum Target {
    Server,
    PeerClient,
}
#[cfg(feature = "client")]

fn main() -> Result<(), std::io::Error> {
    use bincode::config::{self, Configuration};
    // use std::net::IpAddr;
    // use std::str::FromStr;

    let my_socket = Arc::new(Mutex::new(UdpSocket::bind(ADDR.parse().unwrap())?));
    let arc_sock = my_socket.clone();
    let poll = Arc::new(Mutex::new(Poll::new()?));
    let arc_poll = poll.clone();
    let client_peer_info: Arc<Mutex<Option<SocketAddr>>> = Arc::new(Mutex::new(None));
    let arc_peer_info = client_peer_info.clone();
    let (csx, crx) = channel();
    let (nsx, nrx) = channel();
    arc_poll
        .lock()
        .unwrap()
        .registry()
        .register(
            arc_sock.lock().unwrap().deref_mut(),
            MY_TOKEN,
            Interest::READABLE,
        )
        .unwrap();
    spawn(move || {
        let mut target: Target = Target::Server;
        loop {
            match nrx.recv_timeout(Duration::from_millis(2)) {
                Ok(o) => target = o,
                Err(_e) => {}
            }
            match target {
                Target::Server => {
                    match arc_sock
                        .lock()
                        .unwrap()
                        .connect(SERVER_ADDR.parse().unwrap())
                    {
                        Ok(_) => {
                            println!("connect {:?}", SERVER_ADDR);
                        }
                        Err(e) => {
                            println!("{}", e.to_string());
                            continue;
                        }
                    }
                }
                Target::PeerClient => {
                    if let Some(addr) = arc_peer_info.lock().unwrap().clone() {
                        match arc_sock.lock().unwrap().connect(addr) {
                            Ok(_) => {
                                println!("connect {:?}", addr.to_string());
                            }
                            Err(e) => {
                                println!("{}", e.to_string());
                                continue;
                            }
                        }
                    }
                }
            }
            if let Err(e) = arc_poll.lock().unwrap().registry().reregister(
                arc_sock.lock().unwrap().deref_mut(),
                MY_TOKEN,
                Interest::READABLE | Interest::WRITABLE,
            ) {
                println!("{}", e.to_string());
                continue;
            };
            match csx.send(target.clone()) {
                Ok(_) => {}
                Err(e) => {
                    println!("{}", e.to_string())
                }
            };
        }
    });

    let recv_client_peer_info = || {
        let mut buf = [0; 1500];
        if let Ok(siz) = my_socket.lock().unwrap().recv(buf.as_mut_slice()) {
            match bincode::decode_from_slice::<(String, u16), Configuration>(
                &buf[0..siz],
                config::standard(),
            ) {
                Ok(((ip, port), _)) => {
                    if let Ok(i) = ip.parse() {
                        *client_peer_info.lock().unwrap().deref_mut() =
                            Some(SocketAddr::new(i, port));
                        match nsx.send(Target::PeerClient) {
                            Ok(_) => {}
                            Err(e) => {
                                println!("{}", e.to_string())
                            }
                        };
                    }
                }
                Err(_) => {}
            }
        }
    };
    let mut events = Events::with_capacity(128);
    loop {
        poll.lock()
            .unwrap()
            .poll(&mut events, Some(Duration::from_millis(2)))
            .unwrap();

        for event in events.iter() {
            if event.is_readable() {
                println!("event.is_readable");
                match crx.recv_timeout(Duration::from_millis(2)) {
                    Ok(t) => {
                        println!("{:?}", t);
                        match t {
                            Target::Server => {
                                recv_client_peer_info();
                            }
                            Target::PeerClient => {
                                let mut buf = [0; 1500];
                                match my_socket.lock().unwrap().recv(buf.as_mut_slice()) {
                                    Ok(siz) => {
                                        println!(
                                            "----------{}----------",
                                            String::from_utf8_lossy(&buf[0..siz])
                                        )
                                    }
                                    Err(e) => {
                                        println!("{}", e.to_string())
                                    }
                                }
                            }
                        }
                    }
                    Err(e) => {
                        println!("{}", e.to_string())
                    }
                }
            } else if event.is_writable() {
                println!("event.is_writable");
                match crx.recv_timeout(Duration::from_millis(2)) {
                    Ok(t) => {
                        println!("{:?}", t);
                        let send_data = |data: &str| {
                            let target =
                                if data.eq("ping") && client_peer_info.lock().unwrap().is_none() {
                                    SERVER_ADDR.parse().unwrap()
                                } else {
                                    client_peer_info.lock().unwrap().unwrap()
                                };
                            let o = client_peer_info.lock().unwrap().clone();
                            println!("send data {:?} to {:?}", data, o);
                            match my_socket.lock().unwrap().send_to(data.as_bytes(), target) {
                                Ok(_) => {}
                                Err(e) => {
                                    println!("{}", e.to_string())
                                }
                            };
                        };
                        match t {
                            Target::Server => send_data("ping"),
                            Target::PeerClient => send_data("hello"),
                        }
                    }
                    Err(e) => {
                        println!("{}", e.to_string())
                    }
                }
            }
        }
    }
}
